import { defineComponent, ref, computed, watchEffect, onMounted, onBeforeUnmount, createVNode } from 'vue';
import Popover from '../../Popover/index.js';
import Draggable from '../../Draggable/index.js';
import formFieldRef from '../../use/formFieldRef.js';

const index = /* @__PURE__ */ defineComponent({
  name: "Slider",
  props: {
    range: {
      type: Boolean
    },
    min: {
      type: Number
    },
    max: {
      type: Number
    },
    step: {
      type: Number
    },
    modelValue: {
      type: [Number, Array]
    },
    disabled: {
      type: Boolean
    },
    tipFormatter: {
      type: Function
    },
    marks: {
      type: Object
    },
    asFormField: {
      type: Boolean,
      default: true
    }
  },
  emits: ['update:modelValue', 'change'],
  setup(props, {
    emit
  }) {
    let rail;
    let leftDrag;
    let rightDrag;
    let pop;
    let popRight;
    const wrap = ref();
    const min = props.min ?? 0;
    const max = props.max ?? 100;
    const step = props.step ?? 1.00;
    const range = props.range ?? false;
    const value = formFieldRef(props, emit, range ? [0, 0] : 0);
    const classList = computed(() => ({
      'cm-slider': true,
      'cm-slider-disabled': props.disabled
    }));
    const snap = () => {
      const rect = rail.getBoundingClientRect();
      const allW = rect.width;
      return allW / (max - min) * step;
    };

    // 根据值计算位置
    const calculateLeftRight = computed(() => {
      const val = range ? value.value : [min, value.value];
      const trackWidth = Math.abs(val[1] - val[0]) / (max - min) * 100;
      const trackLeft = (val[0] - min) / (max - min) * 100;
      const handleRight = (val[1] - min) / (max - min) * 100;
      return {
        left: trackLeft,
        width: trackWidth,
        right: handleRight
      };
    });
    const trackStyle = computed(() => {
      const ret = calculateLeftRight.value;
      return {
        left: ret.left + '%',
        width: ret.width + '%'
      };
    });
    const contextLeft = computed(() => {
      const v = range ? value.value[0] : value.value;
      if (props.tipFormatter) {
        return props.tipFormatter(v);
      }
      return `${v}`;
    });
    const contextRight = computed(() => {
      if (props.tipFormatter) {
        return props.tipFormatter(value.value[1]);
      }
      return `${value.value[1]}`;
    });
    const updateHandlerPosition = () => {
      if (!rail) {
        return;
      }
      const ret = calculateLeftRight.value;
      const rect = rail.getBoundingClientRect();
      const leftX = range ? rect.width * ret.left / 100 : rect.width * ret.right / 100;
      const rightX = range ? rect.width * (ret.left + ret.width) / 100 : 0;
      if (leftDrag) {
        leftDrag.setPosition({
          x: leftX,
          y: 0
        });
      }
      if (rightDrag) {
        rightDrag.setPosition({
          x: rightX,
          y: 0
        });
      }
    };

    // 值改变后同步拖拽点的位置
    watchEffect(() => {
      value.value;
      updateHandlerPosition();
    });
    onMounted(() => {
      // 容器尺寸变化的时候改变值的位置
      const ob = new ResizeObserver(() => {
        updateHandlerPosition();
      });
      ob.observe(wrap.value);
      onBeforeUnmount(() => ob.disconnect());
    });
    const toFixed = num => {
      let r;
      try {
        r = step.toString().split('.')[1].length;
      } catch (e) {
        r = 0;
      }
      const m = Math.pow(10, r);
      return Math.round(num * m) / m;
    };

    //左侧拖拽
    const onLeftDrag = (e, data) => {
      const railRect = rail.getBoundingClientRect();
      const allW = railRect.width;
      const v = toFixed(data.x / allW * (max - min) + min);
      setTimeout(() => {
        pop && pop.updatePosition();
      });
      if (range && v > value.value[1]) {
        return false;
      }
      const val = range ? [v, Math.max(v, value.value[1])] : v;
      value.value = val;
      emit('change', val);
    };

    //右侧拖拽
    const onRightDrag = (e, data) => {
      const railRect = rail.getBoundingClientRect();
      const allW = railRect.width;
      const v = toFixed(data.x / allW * (max - min) + min);
      setTimeout(() => {
        popRight && popRight.updatePosition();
      });
      if (range && v < value.value[0]) {
        return false;
      }
      const val = range ? [Math.min(value.value[0], v), v] : v;
      value.value = val;
      emit('change', val);
    };

    // 点击后改变值
    const onMouseDown = e => {
      if (props.disabled) {
        return;
      }
      if (e.target.classList.contains('cm-slider-handle')) {
        return;
      }
      const slider = e.target.closest('.cm-slider');
      if (!slider) {
        return;
      }
      const sliderRect = slider.getBoundingClientRect();
      const x = e.pageX - sliderRect.left;
      const railRect = rail.getBoundingClientRect();
      const allW = railRect.width;
      const stepNum = Math.round(x / allW * (max - min) / step);
      const v = toFixed(stepNum * step + min);
      let val = value.value;
      if (range) {
        const nearLeft = Math.abs(val[1] - v) > Math.abs(val[0] - v);
        val = nearLeft ? [v, val[1]] : [val[0], v];
        value.value = val;
        setTimeout(() => {
          nearLeft ? pop && pop.updatePosition() : popRight && popRight.updatePosition();
        });
        emit('change', val);
      } else {
        value.value = v;
        setTimeout(() => {
          pop && pop.updatePosition();
        });
        emit('change', v);
      }
    };
    const steps = computed(() => {
      if (!props.marks) {
        return [];
      }
      const arr = [];
      for (let i = min; i <= max; i += step) {
        if (props.marks[i]) {
          arr.push(i);
        }
      }
      return arr;
    });
    const marks = computed(() => {
      if (props.marks) {
        const arr = [];
        for (const step in props.marks) {
          arr.push({
            step: parseFloat(step),
            label: props.marks[step]
          });
        }
        return arr;
      }
      return [];
    });
    return () => createVNode("div", {
      "class": classList.value,
      "onMousedown": onMouseDown,
      "ref": wrap
    }, [createVNode("div", {
      "class": "cm-slider-rail",
      "ref": el => rail = el
    }, null), createVNode("div", {
      "class": "cm-slider-track",
      "style": trackStyle.value
    }, null), createVNode("div", {
      "class": "cm-slider-steps"
    }, [steps.value.map(item => {
      const ranges = range ? value.value : [min, value.value];
      const isActive = item >= ranges[0] && item <= ranges[1];
      const stepClass = () => ({
        'cm-slider-step': true,
        'cm-slider-step-active': isActive
      });
      const left = `${(item - min) / (max - min) * 100}%`;
      return createVNode("span", {
        "class": stepClass(),
        "style": {
          left
        }
      }, null);
    })]), createVNode(Popover, {
      "disabled": props.disabled,
      "content": contextLeft.value,
      "align": "top",
      "ref": el => pop = el,
      "arrow": true
    }, {
      default: () => [createVNode(Draggable, {
        "axis": "x",
        "disabled": props.disabled,
        "ref": el => leftDrag = el,
        "onDrag": onLeftDrag,
        "bounds": "parent",
        "class": "cm-slider-handle-drag",
        "grid": [snap(), snap()]
      }, {
        default: () => [createVNode("div", {
          "class": "cm-slider-handle",
          "tab-index": "0"
        }, null)]
      })]
    }), range ? createVNode(Popover, {
      "disabled": props.disabled,
      "content": contextRight.value,
      "align": "top",
      "ref": el => popRight = el,
      "arrow": true
    }, {
      default: () => [createVNode(Draggable, {
        "axis": "x",
        "disabled": props.disabled,
        "ref": el => rightDrag = el,
        "onDrag": onRightDrag,
        "bounds": "parent",
        "class": "cm-slider-handle-drag",
        "grid": [snap(), snap()]
      }, {
        default: () => [createVNode("div", {
          "class": "cm-slider-handle",
          "tab-index": "1"
        }, null)]
      })]
    }) : null, props.marks ? createVNode("div", {
      "class": "cm-slider-marks"
    }, [marks.value.map(item => {
      const left = `${(item.step - min) / (max - min) * 100}%`;
      return createVNode("span", {
        "class": "cm-slider-mark",
        "style": {
          left
        }
      }, [item.label]);
    })]) : null]);
  }
});

export { index as default };
